﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;

namespace Mbs.Net
{
    public interface IServiceReceiver : IServiceHandler
    {
        void OnReceived(byte[] packet);
    }

    public interface IPacketServiceReceiver : IServiceReceiver
    {
        ICoding Coding { get; }
        void OnReceived(IPacket packet);
    }

    public class ServiceReceiver : ServiceHandler, IServiceReceiver
    {
        internal SocketAsyncEventArgs ReceiveAsyncEvent { get; private set; }
        protected PacketParser Parser { get; private set; }
       
        protected override void OnInit()
        {
            ReceiveAsyncEvent = new SocketAsyncEventArgs();
            ReceiveAsyncEvent.Completed += this.OnReceivedCompleted;
            Parser = new PacketParser(SR.EOF, ParsePacket);

            if (Socket.Connected)
            {
                Socket.InvokeAsyncMethod(Socket.ReceiveAsync, OnReceivedCompleted, ReceiveAsyncEvent, OnExceptionFired);
            }
        }

        private void ParsePacket(byte[] packet)
        {
            if (IsHeartPacket(packet))
            {
                Host.Sender.Send(packet);
                OnReceivedHeartPacket(packet);
            }
            else
                OnReceived(packet);
        }

        private void OnReceivedCompleted(object sender, SocketAsyncEventArgs args)
        {
            try
            {
                if (args.BytesTransferred == 0)
                {
                    Host.Close();
                    return;
                }
                if (args.SocketError != SocketError.Success)
                {
                    Host.Close();
                    return;
                }

                Byte[] data = new Byte[args.BytesTransferred];
                Array.Copy(args.Buffer, args.Offset, data, 0, data.Length);

                Parser.Parse(data);

                if (Socket.Connected)
                {
                    Socket.InvokeAsyncMethod(Socket.ReceiveAsync, OnReceivedCompleted, ReceiveAsyncEvent, OnExceptionFired);
                }
            }
            catch (Exception ex)
            {
                OnExceptionFired(ex);
            }
        }

        public virtual void OnReceived(byte[] data)
        {
        }

        protected virtual void OnReceivedHeartPacket(byte[] packet)
        {

        }

        protected bool IsHeartPacket(byte[] packet)
        {
            int length = Info.HeartPacket.Length;
            if (packet == null || packet.Length != length)
                return false;

            for (int i = 0; i < length; i++)
                if (packet[i] != Info.HeartPacket[i])
                    return false;
            return true;
        }
    }

    public class PacketServiceReceiver : IPacketServiceReceiver
    {
        private IServiceReceiver ByteReceiver;

        public PacketServiceReceiver(IServiceReceiver byteReceiver)
        {
            ByteReceiver = byteReceiver;
        }

        protected virtual void OnInit() { }

        #region IPacketServiceReceiver Members

        public ICoding Coding { get; set; }

        public virtual void OnReceived(IPacket packet)
        {
        }

        #endregion

        #region IServiceReceiver Members

        public void OnReceived(byte[] data)
        {
            ByteReceiver.OnReceived(data);
            var packet = Coding.DecodePacket(data);
            if (packet != null)
            {
                OnReceived(packet);
            }
        }

        #endregion

        #region IServiceHandler Members

        public Mbs.Net.Tcp.HostInfo Info
        {
            get { return ByteReceiver.Info; }
        }

        

        public IServiceHost Host
        {
            get { return ByteReceiver.Host; }
        }

        public void Init(Mbs.Net.Tcp.HostInfo info, IServiceHost host, Socket socket)
        {
            if (Coding == null)
                throw new NullReferenceException("Coding");

            ByteReceiver.Init(info, host, socket);

            OnInit();
        }

        #endregion
    }

}
